#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ui_login.h"
#include "dialogcode.h"
#include "ui_dialogcode.h"
#include <QInputDialog>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netdb.h>

QString code("a");

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
	loginUi = new Ui::Login;
    loginUi->setupUi(this);
	
	connect(loginUi->loginbutton, SIGNAL(clicked()), this, SLOT(login()));
	this->show();
}

unsigned char* MainWindow::decode_base64(const char* in , int* len)
{
 	unsigned int n , t = 0 , c = 0;
	unsigned char* res;
	unsigned char out[3];
	unsigned char inp[4];

	n = strlen(in);
	if(n % 4 != 0)
	{
		debug_error("Try to decode a string which is not a base64 string(decode_base64)");
		return NULL;
	}
	n = n / 4 * 3;
	if(len != NULL)
		*len = n;
	res = (unsigned char*)malloc(n);
	memset(res , 0 , n);
	while(1)
	{
		memset(inp , 0 , 4);
		memset(out , 0 , 3);
		memcpy(inp , in + c , 4);
		c += 4;
		n = EVP_DecodeBlock(out , inp , 4 );
		memcpy(res + t , out , n);
		t += n;
		if(c >= strlen(in))
			break;
	}
	return res;
}

char* MainWindow::get_ip_by_name(const char* hostname)
{
	struct hostent *hst;
	char *name , *pos;
	int len;
	char* ip = (char*)malloc(20);
	memset(ip , 0 , 20);
	pos = strstr(hostname , "//");
	if(pos != NULL)
		pos += 2;
	else
		pos = (char*)hostname;
	if(strstr(pos , "/") != NULL)
		len = strlen(pos) - strlen(strstr(pos , "/"));
	else
		len = strlen(pos);
	name = (char*)malloc(len + 1);
	memset(name , 0 , len + 1);
	strncpy(name , pos , len);
reget:
	hst = gethostbyname(name);
	if(hst == NULL)
		goto reget;
	inet_ntop(AF_INET , hst->h_addr , ip , 16);
	free(name);
	return ip;
}

char* MainWindow::http_connection_get_response(FetionConnection* conn)
{
	char buf[1024*4];
	char ls[10];
	char *pos;
	char *res;
	int  n = 0 , c , len;

	memset(buf, 0, sizeof(buf));

	do{
		c = tcp_connection_recv(conn
			, buf + n , sizeof(buf) - n - 1);
		n += c;
		if(n >= sizeof(buf))
			return NULL;
	}while(!strstr(buf , "\r\n\r\n"));

	pos = strstr(buf , "Content-Length: ") + 16;
	len = strlen(pos) - strlen(strstr(pos , "\r\n"));
	memset(ls , 0 , sizeof(ls));
	strncpy(ls , pos , len);
	len = atoi(ls);

	pos = strstr(pos , "\r\n\r\n") + 4;
	n = strlen(pos);

	res = (char*)malloc(len + 1);
	if(res == NULL){
		return NULL;
	}
	memset(res , 0 , len + 1);
	strcpy(res , pos);

	for(;;){
		memset(buf , 0 , sizeof(buf));
		c = tcp_connection_recv(conn , buf , sizeof(buf) - 1);
		if(c <= 0)
			break;
		strcpy(res + n, buf);
		n += c;
		if(n >= len)
			break;
	}

	return res;
}

void MainWindow::generate_pic_code(User* user)
{
	char buf[1024] , *res , *code;
	char codePath[128];
	char cookie[1024];
	char* ip;

	FILE* picfd;
	int piclen = 0;
	unsigned char* pic;
	int n;
	Config *config = user->config;

	xmlDocPtr doc;
	xmlNodePtr node;

	memset(buf , 0 , sizeof(buf));
	ip = get_ip_by_name(NAVIGATION_URI);
	FetionConnection* con = tcp_connection_new();

	if(config->proxy != NULL && config->proxy->proxyEnabled)
		tcp_connection_connect_with_proxy(con , ip,
				80 , config->proxy);
	else
		tcp_connection_connect(con , ip , 80);

	memset(cookie , 0 , sizeof(cookie));
	if(user->ssic){
		sprintf(cookie , "Cookie: ssic=%s\r\n" , user->ssic);
	}
	sprintf(buf , "GET /nav/GetPicCodeV4.aspx?algorithm=%s HTTP/1.1\r\n"
				  "%sHost: %s\r\n"
				  "User-Agent: IIC2.0/PC "PROTO_VERSION"\r\n"
				  "Connection: close\r\n\r\n"
				, user->verification->algorithm == NULL ? "" : user->verification->algorithm
				, user->ssic == NULL ? "" : cookie , NAVIGATION_URI);
	tcp_connection_send(con , buf , strlen(buf));
	res = http_connection_get_response(con);
	tcp_connection_free(con);
	doc = xmlParseMemory(res , strlen(res));
	node = xmlDocGetRootElement(doc);
	node = node->xmlChildrenNode;
	user->verification->guid = (char*)xmlGetProp(node , BAD_CAST "id");
	code = (char*)xmlGetProp(node , BAD_CAST "pic");
	xmlFreeDoc(doc);
	debug_info("Generating verification code picture");
	pic = decode_base64(code , &piclen);
	free(code);
	memset(codePath, 0, sizeof(codePath));
	sprintf(codePath , "%s/code.gif" , user->config->globalPath);
	picfd = fopen(codePath , "wb+");
	n = 0;
	for(; n != piclen ;){
		n += fwrite(pic + n , 1 , piclen - n , picfd);
	}
	fclose(picfd);
	free(res);
}

int MainWindow::login()
{
	FetionConnection *conn = NULL;
	QString pno;
	QString pwd;
	int state;
	char *pos;
	char *nonce;
	char *key;
	char *aeskey;
	char *response;
	//userlist *newul;
	//userlist *ul_cur;
	int local_group_count;
	int local_buddy_count;
	int new_group_count;
	int new_buddy_count;
	
	//QString text;
	//QInputDialog::getText(this, "a", "bb", QLineEdit::Normal, text);
	
	pno = loginUi->username->text();
	pwd = loginUi->password->text();
	int index = loginUi->statecombo->currentIndex();
	switch (index)
	{
	case 0:
		state = P_ONLINE;
		break;
	case 1:
		state = P_BUSY;
		break;
	case 2:
		state = P_AWAY;
		break;
	case 3:
		state = P_HIDDEN;
		break;
	}
	
	QRegExp regExp;
	regExp.setPattern("\\D");
	if (pno.isEmpty() || pwd.isEmpty() || (regExp.indexIn(pno) >= 0))
	{
		printf("login: error input\n");
		showError(tr("输入错误"));
		return -1;
	}

	QByteArray bPno = pno.toLatin1();
	char *cPno = bPno.data();
	QByteArray bPwd = pwd.toLatin1();
	char *cPwd = bPwd.data();
	user = fetion_user_new(cPno, cPwd);
	config = fetion_config_new();
	
	if (!user)
	{
		printf("login: fetion_user_new error\n");
		showError(tr("登录失败"));
		return -1;
	}
	//config->proxy->proxyEnabled = 0;
	fetion_user_set_config(user, config);
	
login:
	pos = ssi_auth_action(user);
	if (!pos)
	{
		printf("login: ssi_auth_action error\n");
		showError(tr("登录失败"));
		return -1;
	}
	parse_ssi_auth_response(pos, user);
	free(pos);
	
	if (user->loginStatus == 421 || user->loginStatus == 420)
	{
		// code
		//printf("code here\n");
		printf("code appear for first time\n");
		printf("%s\n", user->verification->text);
		printf("%s\n", user->verification->tips);
		
	} else if (user->loginStatus == 401 || user->loginStatus == 400 || user->loginStatus == 404)
	{
		printf("login: password ERROR!!\n");
		showError(tr("登录失败。电话号码或者密码错误。"));
		return -1;
	}
	
	QString userid(user->userId);
	loginUi->debug->append(userid);
	
	fetion_config_initialize(config, user->userId);
	
	// no user list now
	// TODO
	
	// download xml configuration file from server
	fetion_config_load(user);
	if (config->sipcProxyPort == 0)
		showMsg(tr("这是您第一次登录，正在下载配置文件"));
	else
		showMsg(tr("正在下载配置文件"));
	
	if (fetion_config_download_configuration(user) < 0)
	{
		showError(tr("连接被中断"));
		return -1;
	}
	fetion_config_save(user);
	fetion_user_set_st(user, state);
	
	fetion_user_load(user);
	fetion_contact_load(user, &local_group_count, &local_buddy_count);
	
	conn = tcp_connection_new();
	showMsg(tr("正在连接服务器"));
	tcp_connection_connect(conn, config->sipcProxyIP, config->sipcProxyPort);
	printf("server %s:%d\n", config->sipcProxyIP, config->sipcProxyPort);
	
	/* add the connection object into the connection list */
	//fx_conn_append(conn);
	
	/* initialize a sip object */
	FetionSip* sip = fetion_sip_new(conn , user->sId);
	
	fetion_user_set_sip(user, sip);
	//fx_login_show_msg(fxlogin, _("Registering to SIPC Server"));
	pos = sipc_reg_action(user);
	
	if(!pos){
		showError(tr("登录失败"));
		return -1;
	}
	
	parse_sipc_reg_response(pos , &nonce , &key);
	free(pos);
	
	aeskey = generate_aes_key();
	if(aeskey == NULL){
		return -1;
	}
	response = generate_response(nonce, user->userId,
					user->password, key, aeskey);
	free(nonce);
	free(key);
	free(aeskey);
	
	// start sipc authentication using the response created just now
	//fx_login_show_msg(fxlogin , _("SIPC Indentify"));
	printf("SIPC Indentify\n");

auth:
	pos = sipc_aut_action(user, response);
	if(pos == NULL){
		showError(tr("登录失败"));
		return -1;
	}
	
	if(parse_sipc_auth_response(pos, user,	&new_group_count, &new_buddy_count) < 0){
		printf("Password error, login failed!!!\n");
		showError(tr("密码错误"));
		return -1;
	}
	free(pos); 
	pos = NULL;
	
	if(user->loginStatus == 401 || user->loginStatus == 400)
	{
		printf("Password error , login failed!!!\n");
		//fx_login_show_err(fxlogin , _("Authenticate failed."));
		return -1;
	}
	
	if(user->loginStatus == 421 || user->loginStatus == 420)
	{
		printf("%s\n", user->verification->text);
		printf("%s\n", user->verification->tips);
		
		showMsg(tr("正在获取图片，请等待..."));
		generate_pic_code(user);
		
		DialogCode dlc(this, QString(user->config->globalPath));
		dlc.exec();
		
		QByteArray bCode = code.toLatin1();
		char *cCode = bCode.data();
		
		printf("Input verfication code:%s\n", cCode);
		fetion_user_set_verification_code(user, cCode);
		goto auth;
	}
#if 0
	Contact *c_cur;
	Contact *c_tmp;
	Group   *g_cur;
	Group   *g_tmp;
	
	// update buddy list
	if(user->groupCount == 0)
		user->groupCount = local_group_count;
	else if(user->groupCount != local_group_count){
		for (g_cur = user->groupList->next; g_cur != user->groupList; )
		{
			g_tmp = g_cur;
			g_cur = g_cur->next;
			if(!g_tmp->dirty)
			{
				fetion_group_list_remove(g_tmp);
				free(g_tmp);
			}
		}
	}
	
		/* update buddy count */
		if(user->contactCount == 0)
			user->contactCount = local_buddy_count;
		else if(user->contactCount != local_buddy_count){
			/* do some clean on the local buddy data */
			for(c_cur = user->contactList->next;
					c_cur !=  user->contactList;){
				c_tmp = c_cur;
				c_cur = c_cur->next;
				if(!c_tmp->dirty){
					fetion_contact_list_remove(c_tmp);
					g_free(c_tmp);
				}
			}
		}
	
		fx_login_show_msg(fxlogin , _("Initializing main panel"));
		
		pg_group_get_list(user);
	
		popup_startup_msg(fxmain);
	
		fx_login_show_msg(fxlogin , _("Login sucessful"));
	
		/*if there is not a buddylist name "Ungrouped" or "Strangers", create one */
		if(fetion_group_list_find_by_id(user->groupList,
				BUDDY_LIST_NOT_GROUPED) == NULL &&
				fetion_contact_has_ungrouped(user->contactList)){
			group = fetion_group_new();
			group->groupid = BUDDY_LIST_NOT_GROUPED;
			strcpy(group->groupname , N_("Ungrouped"));
			fetion_group_list_append(user->groupList , group);
		}
		if(fetion_group_list_find_by_id(user->groupList,
				BUDDY_LIST_STRANGER) == NULL &&
				fetion_contact_has_strangers(user->contactList)){
			group = fetion_group_new();
			group->groupid = BUDDY_LIST_STRANGER;
			strcpy(group->groupname , N_("Strangers"));
			fetion_group_list_prepend(user->groupList , group);
		}
		fx_login_show_msg(fxlogin , _("Initializing main panel"));
	
		/* initialize head panel */
		gdk_threads_enter();
		fx_head_initialize(fxmain);
		gdk_threads_leave();
	
		/* initialize main panel which in fact only contains a treeview*/
		gdk_threads_enter();
		fxmain->mainPanel = fx_tree_new();
		fx_tree_initilize(fxmain);
		gdk_threads_leave();
	
		/* initialize bottom panel */
		gdk_threads_enter();
		fx_bottom_initialize(fxmain);
		gdk_threads_leave();
	
		gdk_threads_enter();
		gtk_window_set_resizable(GTK_WINDOW(fxmain->window) , TRUE);
		fetion_config_load_size(config);
		gtk_window_resize(GTK_WINDOW(fxmain->window),
				config->window_width,
				config->window_height);
		gdk_threads_leave();
	
		/* set tooltip of status icon */
		sprintf(statusTooltip, "%s\n%s",
				 user->nickname , user->mobileno);
	
		gdk_threads_enter();
		gtk_status_icon_set_tooltip(
				GTK_STATUS_ICON(fxmain->trayIcon),
				statusTooltip);
		/* set title of main window*/
		gtk_window_set_title(GTK_WINDOW(fxmain->window),
						user->nickname );
		gdk_threads_leave();
	
		gdk_threads_enter();
		fx_login_free(fxlogin);
		fx_head_show(fxmain);
		fx_tree_show(fxmain);
		fx_bottom_show(fxmain);
		gdk_threads_leave();
	
		/* start sending keep alive request periodically */
		g_timeout_add_seconds(70 , (GSourceFunc)fx_main_register_func , user);
		g_timeout_add_seconds(5 , (GSourceFunc)fx_main_check_func , fxmain);
	
		g_thread_exit(0);
#endif
	return 0;
}

MainWindow::~MainWindow()
{
    delete loginUi;
}
